﻿//  --------------------------------
//  Copyright (c) AW Systems, Inc. All rights reserved.
//  This source code is made available under the terms of the Microsoft Public License (Ms-PL)
//  http://www.codeplex.com/SLAM/license
//  ---------------------------------

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data.Linq;
using System.Linq;
using AWS.SLAM.Administration.Data;
using AWS.SLAM.Configuration;
using AWS.SLAM.SharePoint;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;

namespace AWS.SLAM.Administration
{
    public partial class Configuration : LayoutsPageBase
    {   
        private string slamFolder = "";

        protected void Page_Load(object sender, EventArgs e)
        {
            //if under a site collection other than the root, doing a simple
            //redirect to the page will always redirect to the root, so the desired
            //location needs extracted from the RawUrl
            slamFolder = Request.RawUrl.Substring(0, Request.RawUrl.LastIndexOf("/") + 1);

            if (!IsPostBack)
            {
                //Test if the 3.5 Framework is installed
                if (!InstallationValidator.VerifyNetFx35IsInstalled())
                {
                    Response.Redirect(slamFolder + "SLAMControlPanel.aspx");
                }
                else
                {
                    //Test if SLAM can clear instances, if it cannot go back to the Control Panel
                    try
                    {
                        ComponentService.ClearInstances();
                    }
                    catch
                    {
                        Response.Redirect(slamFolder + "SLAMControlPanel.aspx");
                    }
                }
                validationResults.Visible = false;
            }
            else
            {
                ComponentService.ClearInstances();
            }
        }


        /// <summary>
        /// Event handler for validating the SLAM configuration
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void btnValidateConfiguration_Click(object sender, EventArgs e)
        {
            btnRefresh.Visible = true;
            validationResults.Visible = true;
            optionsPanel.Visible = false;
            
            lblErrorMessages.Text = "";

            //Track all matched types, all unmatched types
            List<string> unmatchedTypes = new List<string>();
            List<string> matchedTypes = new List<string>();

            //Track all matched fields for all matched types 
            //and all unmatched fields for matched types
            List<string> matchedFields = new List<string>();
            List<string> unmatchedFields = new List<string>();

            try
            {
                SPWeb site = SPContext.Current.Web;
                SPSecurity.RunWithElevatedPrivileges(delegate()
                {
                    using (SPSite elevatedSite = new SPSite(site.Site.ID))
                    {
                        SPWeb elevatedWeb = null;
                        //Get SLAM configuration
                        IConfigurationManager configuration = ComponentService.GetInstance<IConfigurationManager>();
                        //Open ConnectionStrings section
                        ConnectionStringSection connections = configuration.GetSection<ConnectionStringSection>("ConnectionStrings");

                        DataContext dataContext = new DataContext(connections.Default);
                        //Check if connection is established, if it cannot be show error
                        try
                        {
                            bool messageTableExists = dataContext.ExecuteQuery<int>(Queries.VerifyMessageLogExists).FirstOrDefault() > 0;
                        }
                        catch (Exception ex)
                        {
                            lblErrorMessages.Text = "SLAM is attempting to connect to the database using the Default connection string defined in the SLAM configuration file but is failing to connect.  It is receiving the following error:<br /><br />" + ex.Message + "<br /><br />";
                        }

                        //Determine what DataMapping to use from the configuration
                        string siteCollection = elevatedSite.ServerRelativeUrl.TrimStart('/');
                        DataMapping currentDataMapping = configuration.DataMapping;
                        if (!String.IsNullOrEmpty(siteCollection) && configuration.DataMappings.ContainsKey(siteCollection))
                        {
                            currentDataMapping = configuration.DataMappings[siteCollection];
                        }

                        foreach (ConfiguredType type in currentDataMapping.GetAllTypes())
                        {
                            //For SqlConfiguredTypes the type name to use for getting type
                            //information from the configuration should be the Table name if present
                            //otherwise it should be the regular type Name.
                            string configuredTypeName = type.Name;
                            if (type is SqlConfiguredType && !String.IsNullOrEmpty(((SqlConfiguredType)type).Table))
                            {
                                configuredTypeName = ((SqlConfiguredType)type).Table;
                            }

                            //For tracking, store a qualified version of the type name in these formats:
                            //Subsite List with ID: Site - ID - Name 
                            //Root List with ID: ID - Name
                            //Root List without ID or Root Content Type: Name
                            //Subsite Content Type: Site - Name
                            string typeString = String.Format("{0} - {1} - {2}", type.Site, type.Id, type.Name);

                            //If Site is empty, open the Root Web
                            if (String.IsNullOrEmpty(type.Site))
                            {
                                elevatedWeb = elevatedSite.RootWeb;

                                if (!String.IsNullOrEmpty(type.Id))
                                {
                                    typeString = String.Format("{0} - {1}", type.Id, type.Name);
                                }
                                else
                                {
                                    typeString = type.Name;
                                }
                            }
                            else
                            {
                                if (String.IsNullOrEmpty(type.Id))
                                {
                                    typeString = String.Format("{0} - {1}", type.Site, type.Name);
                                }

                                try
                                {
                                    elevatedWeb = elevatedSite.AllWebs[type.Site];

                                    //If Web does not exist, track type as unmatched
                                    if (!elevatedWeb.Exists)
                                    {
                                        unmatchedTypes.Add(typeString + " (Could not open Site)");
                                    }
                                }
                                catch
                                {
                                    //If opening the Web for type.Site throws an exception, 
                                    //track type as unmatched
                                    unmatchedTypes.Add(typeString + " (Could not open Site)");
                                }
                            }

                            if (elevatedWeb != null && elevatedWeb.Exists)
                            {
                                if (type.ContentType)
                                {
                                    try
                                    {
                                        SPContentType contentType = null;
                                        try
                                        {
                                            contentType = elevatedWeb.ContentTypes[type.Name];
                                        }
                                        catch 
                                        { 
                                            //If it fails to here, the contentType is obviously
                                            //not matched and will be tracked as such below
                                        }
                                        if (contentType != null)
                                        {
                                            SPListItem listItem = null;

                                            //To determine which configured fields match
                                            //we need an SPListItem.  To get one even
                                            //if all lists are empty, find a list that contains
                                            //this content type and add a new SPListItem to it
                                            //that we will not perform an Update against
                                            for (int i = 0; i < elevatedSite.AllWebs.Count; i++)
                                            {
                                                SPWeb subWeb = elevatedSite.AllWebs[i];
                                                SPContentTypeCollection listContentTypes = null;
                                                foreach (SPList list in subWeb.Lists)
                                                {
                                                    listContentTypes = null;
                                                    try
                                                    {
                                                        listContentTypes = list.ContentTypes;
                                                    }
                                                    catch (Exception ex)
                                                    {
                                                        //lblErrorMessages.Text += "<br />Type: " + typeString + "<br />List: " + list.Title + "<br />Exception: " + ex.ToString();
                                                    }

                                                    if (listContentTypes != null && listContentTypes.Contains(contentType))
                                                    {
                                                        //If content type is found, track it as matched
                                                        if (!matchedTypes.Contains(typeString))
                                                        {
                                                            matchedTypes.Add(typeString);
                                                        }

                                                        listItem = list.Items.Add();
                                                        listItem["ContentTypeId"] = contentType.Id;

                                                        if (listItem != null)
                                                        {
                                                            List<Field> typeFields = currentDataMapping.GetTypeFields("", configuredTypeName).ToList();
                                                            List<AssociationField> associationFields = currentDataMapping.GetTypeAssociations("", configuredTypeName).ToList();
                                                            PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(listItem);

                                                            //Iterate through each configured field for the content type
                                                            foreach (Field currentField in typeFields)
                                                            {
                                                                //Because SLAM can handle properties, check if the currentField is a property of the listItem
                                                                PropertyDescriptor property = properties.Find(currentField.Name, false);

                                                                //If the currentField is a matched property of a matched listItem Field, track it as 
                                                                //matched, otherwise track it as unmatched
                                                                if (property != null || listItem.Fields.ContainsField(currentField.Name))
                                                                {
                                                                    if (!matchedFields.Contains(String.Format("{0}: {1}", typeString, currentField.Name)))
                                                                    {
                                                                        matchedFields.Add(String.Format("{0}: {1}", typeString, currentField.Name));
                                                                    }
                                                                }
                                                                else
                                                                {
                                                                    if (!unmatchedFields.Contains(String.Format("{0}: {1}", typeString, currentField.Name)))
                                                                    {
                                                                        unmatchedFields.Add(String.Format("{0}: {1}", typeString, currentField.Name));
                                                                    }
                                                                }
                                                            }

                                                            //Iterate through each configured association field for the content type
                                                            foreach (AssociationField currentField in associationFields)
                                                            {
                                                                //If the currentField is a matched listItem Field, track it as 
                                                                //matched, otherwise track it as unmatched
                                                                if (listItem.Fields.ContainsField(currentField.Name))
                                                                {
                                                                    if (!matchedFields.Contains(String.Format("{0}: {1}", typeString, currentField.Name)))
                                                                    {
                                                                        matchedFields.Add(String.Format("{0}: {1}", typeString, currentField.Name));
                                                                    }
                                                                }
                                                                else
                                                                {
                                                                    if (!unmatchedFields.Contains(String.Format("{0}: {1}", typeString, currentField.Name)))
                                                                    {
                                                                        unmatchedFields.Add(String.Format("{0}: {1}", typeString, currentField.Name));
                                                                    }
                                                                }
                                                            }
                                                        }
                                                    }
                                                }
                                                subWeb.Dispose();
                                            }
                                            if (!matchedTypes.Contains(typeString))
                                            {
                                                //If the content type wasn't found, track it as unmatched
                                                unmatchedTypes.Add(typeString);
                                            }
                                        }   
                                    }
                                    catch (Exception ex)
                                    {
                                        //If processing the content type threw an exception, track it as unmatched
                                        //then show the error
                                        unmatchedTypes.Add(typeString);
                                        lblErrorMessages.Text += "Type: " + typeString + "<br />Exception: " + ex.ToString();
                                    }
                                }
                                else //If not a content type
                                {
                                    string extraMatchedMessage = "";

                                    //Check for a valid configured ID if present
                                    Guid guidId = Guid.Empty;
                                    if (!String.IsNullOrEmpty(type.Id))
                                    {
                                        try
                                        {
                                            guidId = new Guid(type.Id);
                                        }
                                        catch (Exception ex)
                                        {
                                            //If ID is not a valid GUID, show an error and if the type 
                                            //continues to match show the qualification that despite the 
                                            //ID's presence in the configuration the matching is happening
                                            //on the type's Name
                                            lblErrorMessages.Text += String.Format("List ID not a valid Guid.<br />Type: {0}<br />ID: {1}<br /><br />", type.Name, type.Id);
                                            extraMatchedMessage = " (Matched by Name)";
                                        }
                                    }

                                    SPList list = null;
                                    try
                                    {
                                        try
                                        {
                                            if (guidId != Guid.Empty)
                                            {
                                                list = elevatedWeb.Lists[guidId];
                                            }
                                            else
                                            {
                                                list = elevatedWeb.Lists[type.Name];
                                            }
                                        }
                                        catch 
                                        { 
                                            //If it fails to here, the list cannot be found which
                                            //will be tracked below
                                        }

                                        if (list != null)
                                        {
                                            //If the list is found, track it as matched
                                            matchedTypes.Add(typeString + extraMatchedMessage);

                                            //To check if configured fields match an SPListItem
                                            //must exist in the list.  To easily perform the validation
                                            //even on an empty list add a new SPListItem that we will
                                            //not Update
                                            SPListItem listItem = list.Items.Add();

                                            if (listItem != null)
                                            {
                                                List<Field> typeFields = currentDataMapping.GetTypeFields(type.Id, configuredTypeName).ToList();
                                                List<AssociationField> associationFields = currentDataMapping.GetTypeAssociations(type.Id, configuredTypeName).ToList();
                                                PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(listItem);

                                                //Iterate through each configured field for the list
                                                foreach (Field currentField in typeFields)
                                                {
                                                    //Because SLAM can handle properties, check if the currentField is a property of the listItem
                                                    PropertyDescriptor property = properties.Find(currentField.Name, false);

                                                    //If the currentField is a matched property of a matched listItem Field, track it as 
                                                    //matched, otherwise track it as unmatched
                                                    if (property != null || listItem.Fields.ContainsField(currentField.Name))
                                                    {
                                                        if (!matchedFields.Contains(String.Format("{0}: {1}", typeString, currentField.Name)))
                                                        {
                                                            matchedFields.Add(String.Format("{0}: {1}", typeString, currentField.Name));
                                                        }
                                                    }
                                                    else
                                                    {
                                                        if (!unmatchedFields.Contains(String.Format("{0}: {1}", typeString, currentField.Name)))
                                                        {
                                                            unmatchedFields.Add(String.Format("{0}: {1}", typeString, currentField.Name));
                                                        }
                                                    }
                                                }

                                                //Iterate through each configured association field for the list
                                                foreach (AssociationField currentField in associationFields)
                                                {
                                                    //If the currentField is a matched listItem Field, track it as 
                                                    //matched, otherwise track it as unmatched
                                                    if (listItem.Fields.ContainsField(currentField.Name))
                                                    {
                                                        if (!matchedFields.Contains(String.Format("{0}: {1}", typeString, currentField.Name)))
                                                        {
                                                            matchedFields.Add(String.Format("{0}: {1}", typeString, currentField.Name));
                                                        }
                                                    }
                                                    else
                                                    {
                                                        if (!unmatchedFields.Contains(String.Format("{0}: {1}", typeString, currentField.Name)))
                                                        {
                                                            unmatchedFields.Add(String.Format("{0}: {1}", typeString, currentField.Name));
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                        else
                                        {
                                            //If the list wasn't found, track it as unmatched
                                            unmatchedTypes.Add(typeString);
                                        }
                                    }
                                    catch (Exception ex)
                                    {
                                        //If processing the content type threw an exception, track it as unmatched
                                        //then show the error
                                        unmatchedTypes.Add(typeString);
                                        lblErrorMessages.Text += "Type: " + typeString + "<br />Exception: " + ex.ToString();
                                    }
                                }
                            }

                            if (elevatedWeb != null)
                            {
                                elevatedWeb.Dispose();
                            }
                        }
                    }
                });


                //If types were matched, show the results
                rptMatchedTypes.Visible = matchedTypes.Count > 0;
                rptMatchedTypes.DataSource = matchedTypes;
                rptMatchedTypes.DataBind();

                //If types were not matched, show the results
                rptUnmatchedTypes.Visible = unmatchedTypes.Count > 0;
                rptUnmatchedTypes.DataSource = unmatchedTypes;
                rptUnmatchedTypes.DataBind();

                //If fields were matched, show the results
                rptMatchedFields.Visible = matchedFields.Count > 0;
                rptMatchedFields.DataSource = matchedFields;
                rptMatchedFields.DataBind();

                //If fields were not matched, show the results
                rptUnmatchedFields.Visible = unmatchedFields.Count > 0;
                rptUnmatchedFields.DataSource = unmatchedFields;
                rptUnmatchedFields.DataBind();
            }
            catch (Exception ex)
            {
                //In case anything else threw an error, show it
                lblErrorMessages.Text = ex.ToString();
            }
        }

        protected void btnBack_Click(object sender, EventArgs e)
        {   
            Response.Redirect(slamFolder + "SLAMControlPanel.aspx");
        }
    }
}
